<template>
	<view class="content">
		<!-- 验证码主体 -->
		<view v-for="(item, index) in list" :key="index" class="list-item" @longtap="delCode(index)">
			<view class="list-item-left">
				<view class="name" @click="editName(index)">{{item.name}}</view>
				<view class="code" @click="copyCode(item.code)">{{item.code.slice(0, 3) + ' ' + item.code.slice(3, 6)}}
				</view>
			</view>
			<view class="list-item-right">
				<view class="item-time"
					:class="{'primary' : 10 < second, 'warning' : 5 < second && 10 >= second, 'danger' : 5 >= second}">
					{{second}}
				</view>
			</view>
		</view>

		<!-- 空状态 -->
		<view class="empty-box" v-show="0 === list.length">
			<view class="title">设置首个账号</view>
			<view class="title-2">使用双重身份验证设置（Google 或第三方服务）中显示的二维码或设置密钥。</view>
			<view class="empty-box-btn">
				<view class="empty-box-btn-item" @click="scanCode">
					<uni-icons class="icon" type="scan" color="#2979ff" size="30"></uni-icons>
					<view>扫描二维码</view>
				</view>
				<view class="empty-box-btn-item" @click="inputCode">
					<uni-icons class="icon" type="compose" color="#18bc37" size="30"></uni-icons>
					<view>输入设置密钥</view>
				</view>
			</view>
		</view>

		<!-- 悬浮按钮 -->
		<uni-fab :content="fabContent" horizontal="right" vertical="bottom" direction="horizontal"
			@trigger="fabTrigger"></uni-fab>

		<!-- 手动输入框 -->
		<uni-popup ref="popup" background-color="#fff">
			<uni-section class="mb-50" title="输入设置密钥"></uni-section>
			<uni-easyinput class="mb-50" v-model="input.name" placeholder="请输入名称"></uni-easyinput>
			<uni-easyinput class="mb-50" v-model="input.key" placeholder="请输入密钥"></uni-easyinput>
			<button type="primary" @click="inputSubmit">提交</button>
		</uni-popup>

		<!-- 修改名称对话框 -->
		<uni-popup ref="editDialog" type="dialog">
			<uni-popup-dialog mode="input" title="修改名称" :value="edit.name" placeholder="请输入内容"
				@confirm="editConfirm"></uni-popup-dialog>
		</uni-popup>

		<!-- 删除对话框 -->
		<uni-popup ref="delDialog" type="dialog">
			<uni-popup-dialog type="warning" cancelText="取消" confirmText="确认" title="警告" content="确认删除？"
				@confirm="delConfirm"></uni-popup-dialog>
		</uni-popup>

		<!-- 消息提示 -->
		<uni-popup ref="message" type="message">
			<uni-popup-message :type="message.type" :message="message.text" :duration="2000"></uni-popup-message>
		</uni-popup>
	</view>
</template>

<script>
	import {
		sjcl
	} from '@/common/sjcl.js'
	import {
		getCode
	} from '@/common/code_totp.js'
	export default {
		data() {
			return {
				list: [],
				fabContent: [{
						iconPath: '/static/scan.png',
						selectedIconPath: '/static/scan.png',
						text: '扫码',
						active: false
					},
					{
						iconPath: '/static/input.png',
						selectedIconPath: '/static/input.png',
						text: '输入',
						active: false
					}
				],
				second: 0,
				delIndex: -1,
				edit: {
					index: -1,
					name: ''
				},
				message: {
					type: '',
					text: ''
				},
				input: {
					name: '',
					key: ''
				}
			}
		},
		watch: {
			second(newVal, oldVal) {
				if (30 === newVal) {
					this.generateCode()
				}
			}
		},
		onLoad() {
			// 加载缓存数据
			this.loadData()
			// 倒计时
			this.getSecond()
		},
		methods: {
			// 获取缓存数据
			loadData() {
				let data = uni.getStorageSync('MFA-DATA')
				if (data && 0 < data.length) {
					this.list = data
					this.generateCode()
				}
			},
			// 缓存数据
			saveData() {
				uni.setStorageSync('MFA-DATA', this.list)
			},
			// 悬浮按钮点击事件
			fabTrigger(e) {
				let index = e.index
				if (0 === index) {
					this.scanCode()
				} else {
					this.inputCode()
				}
			},
			// 输入方式
			inputCode() {
				this.$refs.popup.open('top')
			},
			// 输入提交
			inputSubmit() {
				let data = this.input
				if (!data.name) {
					this.showMessage('warning', '请输入名称')
					return false
				}
				if (!data.key) {
					this.showMessage('warning', '请输入密钥')
					return false
				}
				if (this.checkRepeat(data.key)) {
					this.showMessage('error', '密钥重复，添加失败')
					return false
				}
				if (!this.checkKey(data.key)) {
					return false
				}
				data.code = getCode(sjcl, data.key, this.getCTime())
				this.list.push(data)
				this.saveData()
				this.showMessage('success', '添加成功')
				this.input = {
					name: '',
					key: ''
				}
				this.$refs.popup.close()
			},
			// 扫码方式
			scanCode() {
				let that = this
				uni.scanCode({
					onlyFromCamera: true,
					scanType: ['qrCode'],
					success(res) {
						let result = res.result
						if (!result) {
							that.showMessage('error', '二维码内容为空')
							return false
						}
						if (!result.startsWith('otpauth://totp/')) {
							that.showMessage('error', '二维码内容错误')
							return false
						}
						result = result.replace('otpauth://totp/', '')
						let name = decodeURI(result.split('?')[0])
						let key = result.split('?')[1].replace('secret=', '')
						if (that.checkRepeat(key)) {
							that.showMessage('error', '密钥重复，添加失败')
							return false
						}
						if (!that.checkKey(key)) {
							return false
						}
						let data = {
							name: name,
							key: key,
							code: getCode(sjcl, key, that.getCTime())
						}
						that.list.push(data)
						that.saveData()
						that.showMessage('success', '添加成功')
					}
				})
			},
			// 检查秘钥
			checkKey(key) {
				try {
					getCode(sjcl, key, this.getCTime())
					return true
				} catch (e) {
					this.showMessage('error', '秘钥错误，' + e.message)
				}
				return false
			},
			// 检查重复
			checkRepeat(key) {
				let flag = false
				for (let i = 0; i < this.list.length; i++) {
					if (key === this.list[i].key) {
						flag = true
						break
					}
				}
				return flag
			},
			// 提示信息
			showMessage(icon, text) {
				uni.showToast({
					title: text,
					icon: icon
				})
			},
			// 倒计时
			getSecond() {
				let that = this
				that.readSecond()
				setInterval(function() {
					that.readSecond()
				}, 1000)
			},
			// 读秒
			readSecond() {
				let date = new Date();
				let second = date.getSeconds();
				if (30 < second) {
					second = second - 30;
				}
				second = 30 - second
				this.second = 0 === second ? 30 : second;
			},
			getCTime() {
				return Math.floor((new Date() - 0) / 30000)
			},
			// 批量生产code
			generateCode() {
				if (!this.list || 0 === this.list.length) {
					return false
				}
				for (let i = 0; i < this.list.length; i++) {
					let data = this.list[i]
					let time = this.getCTime();
					this.list[i].code = getCode(sjcl, data.key, time)
				}
			},
			// 复制code
			copyCode(code) {
				uni.setClipboardData({
					data: code,
					showToast: true
				})
			},
			// 修改用户名
			editName(index) {
				this.edit = {
					index: index,
					name: this.list[index].name
				}
				this.$refs.editDialog.open()
			},
			// 确认修改
			editConfirm(name) {
				if (!name) {
					this.showMessage('error', '请输入名称')
					return false
				}
				this.list[this.edit.index].name = name
				this.saveData()
				this.showMessage('success', '修改成功')
			},
			// 删除数据
			delCode(index) {
				this.delIndex = index
				this.$refs.delDialog.open()
			},
			// 确认删除
			delConfirm() {
				this.list.splice(this.delIndex, 1)
				this.saveData()
			}
		}
	}
</script>

<style>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		padding: 0 30rpx;
	}

	.uni-list {
		width: 100%;
	}

	.list-item {
		width: 100%;
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 20rpx 0;
		border-bottom: 1rpx solid #b9b9b9;
	}

	.list-item-left {
		display: flex;
		flex-direction: column;
		justify-content: center;
		padding: 0 20rpx;
	}

	.list-item-right {
		padding: 0 20rpx;
	}

	.name {
		font-size: 32rpx;
		color: #333;
		margin-bottom: 10rpx;
	}

	.code {
		font-size: 48rpx;
		color: #2979ff;
		font-weight: bold;
		margin-top: 10rpx;
	}

	.item-time {
		width: 40rpx;
		height: 40rpx;
		line-height: 40rpx;
		text-align: center;
		font-size: 24rpx;
		border-radius: 50%;
		color: #fff;
	}

	.primary {
		background-color: #2979ff;
	}

	.warning {
		background-color: #f3a73f;
	}

	.danger {
		background-color: #e43d33;
	}

	.mb-50 {
		margin-bottom: 50rpx;
	}

	.empty-box {
		display: flex;
		flex-direction: column;
		margin-top: 50rpx;
	}

	.empty-box .title {
		color: #333;
		font-size: 36rpx;
		font-weight: bold;
		padding: 30rpx 0;
		text-align: center;
	}

	.empty-box .title-2 {
		color: #999;
		padding: 30rpx 0;
		font-size: 28rpx;
		border-bottom: 1px solid #b9b9b9;
	}

	.empty-box .empty-box-btn {
		display: flex;
		flex-direction: column;
	}

	.empty-box .empty-box-btn .icon {
		width: 50rpx;
		height: 50rpx;
		object-fit: cover;
		margin-right: 20rpx;
		padding: 15rpx 0;
	}

	.empty-box .empty-box-btn .empty-box-btn-item {
		display: flex;
		align-items: center;
		border-bottom: 1px solid #b9b9b9;
		padding: 15rpx 0;
	}
</style>
